package org.dandelion.papercheck.service.impl;

import org.dandelion.papercheck.config.FileStorageProperties;
import org.dandelion.papercheck.common.exception.FileStorageException;
import org.dandelion.papercheck.config.SnowflakeProperties;
import org.dandelion.papercheck.dao.ArticleDao;
import org.dandelion.papercheck.dao.UserDao;
import org.dandelion.papercheck.entity.Article;
import org.dandelion.papercheck.entity.User;
import org.dandelion.papercheck.service.StorageService;
import org.dandelion.papercheck.util.SnowflakeIdWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;

@Service
@Transactional
public class StorageServiceImpl implements StorageService {
    private static final Logger logger = LoggerFactory.getLogger(StorageService.class);

    private final Path fileStorageLocation;
    private final ArticleDao articleDao;
    private final UserDao userDao;
    private final SnowflakeProperties snowflakeProperties;

    @Autowired
    public StorageServiceImpl(FileStorageProperties properties, ArticleDao articleDao, UserDao userDao,
                              SnowflakeProperties snowflakeProperties) {
        this.fileStorageLocation = Paths.get(properties.getUploadDir()).toAbsolutePath().normalize();

        try {
            Files.createDirectories(fileStorageLocation);
        } catch (IOException e) {
            throw new FileStorageException("Could not create dir when store file.");
        }

        this.articleDao = articleDao;
        this.userDao = userDao;
        this.snowflakeProperties = snowflakeProperties;
    }

    @Override
    public Path storeFile(MultipartFile file, String fileName, String username) {
        List<User> userList = userDao.findByUsername(username);

        if (userList.isEmpty()) {
            throw new RuntimeException("can not find user: " + username);
        }

        User user = userList.get(0);

        String originalFileName = fileName;

        if (originalFileName.contains("..")) {
            throw new FileStorageException("File name contains invalid path sequence");
        }

        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(snowflakeProperties.getWorkerId(),
                snowflakeProperties.getDatacenterId());

        fileName = idWorker.nextId() + "-" + originalFileName;

        Path targetPath = getSavePath(fileName);

        try {
            Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
            Article article = new Article();

            article.setFilePath(targetPath.toString());
            article.setOriginalName(originalFileName);
            article.setFileName(fileName);
            article.setUser(user);

            articleDao.save(article);

            return targetPath;
        } catch (IOException e) {
            throw new FileStorageException("Could not store file: " + fileName, e);
        }
    }

    @Override
    public Path getSavePath(String fileName) {
        return fileStorageLocation.resolve(fileName);
    }

    @Override
    public Resource loadFileAsResource(String fileName) {
        Path filePath = fileStorageLocation.resolve(fileName);
        try {
            Resource resource = new UrlResource(filePath.toUri());

            if (!resource.exists()) {
                throw new FileStorageException("File not found: " + fileName);
            }

            return resource;
        } catch (MalformedURLException e) {
            throw new FileStorageException("File not found: " + fileName);
        }
    }

    @Override
    public void updateFingerprint(String fileName, String fingerprint) {
        List<Article> articleList = articleDao.findByFileName(fileName);

        if (articleList.size() > 0) {
            Article article = articleList.get(0);
            article.setFingerprint(fingerprint);
            articleDao.save(article);
        } else {
            throw new FileStorageException("");
        }
    }
}
